#ifndef VECTOR_H
#define VECTOR_H
#include "fixpoint.h"

namespace fixedpoint
{
    struct mat22; //Forward declaration for outerproduct

    struct vec2d
    {
public:
        fixpoint x, y;

        //Constructors
        vec2d(): x(), y() {}
        vec2d(fixpoint X, fixpoint Y): x(X), y(Y) {}
        vec2d(float X, float Y): x(X), y(Y) {}
        vec2d(double X, double Y): x(X), y(Y) {}
        vec2d(int X, int Y): x(X), y(Y) {}
        vec2d(long X, long Y): x(X), y(Y) {}

        //Assigment operators
        vec2d& operator  =(const vec2d& a)
        {
            x  = a.x;
            y  = a.y;
            return *this;
        }
        vec2d& operator +=(const vec2d& a)
        {
            x += a.x;
            y += a.y;
            return *this;
        }
        vec2d& operator -=(const vec2d& a)
        {
            x -= a.x;
            y -= a.y;
            return *this;
        }
        vec2d& operator *=(const vec2d& a)
        {
            x *= a.x;
            y *= a.y;
            return *this;
        }
        vec2d& operator /=(const vec2d& a)
        {
            x /= a.x;
            y /= a.y;
            return *this;
        }
        vec2d& operator |=(const vec2d& a)
        {
            x |= a.x;
            y |= a.y;
            return *this;
        }
        vec2d& operator &=(const vec2d& a)
        {
            x &= a.x;
            y &= a.y;
            return *this;
        }
        vec2d& operator ^=(const vec2d& a)
        {
            x ^= a.x;
            y ^= a.y;
            return *this;
        }
        vec2d& operator *=(const fixpoint a)
        {
            x *= a;
            y *= a;
            return *this;
        }
        vec2d& operator *=(const float a)
        {
            x *= a;
            y *= a;
            return *this;
        }
        vec2d& operator *=(const double a)
        {
            x *= a;
            y *= a;
            return *this;
        }
        vec2d& operator *=(const int a)
        {
            x *= a;
            y *= a;
            return *this;
        }
        vec2d& operator *=(const long a)
        {
            x *= a;
            y *= a;
            return *this;
        }
        vec2d& operator /=(const fixpoint a)
        {
            x /= a;
            y /= a;
            return *this;
        }
        vec2d& operator /=(const float a)
        {
            x /= a;
            y /= a;
            return *this;
        }
        vec2d& operator /=(const double a)
        {
            x /= a;
            y /= a;
            return *this;
        }
        vec2d& operator /=(const int a)
        {
            x /= a;
            y /= a;
            return *this;
        }
        vec2d& operator /=(const long a)
        {
            x /= a;
            y /= a;
            return *this;
        }

        //Basic binary operators. These are all piecewise, including multiplation
        vec2d operator +(const vec2d& a) const
        {
            return vec2d(x + a.x, y + a.y);
        }
        vec2d operator -(const vec2d& a) const
        {
            return vec2d(x - a.x, y - a.y);
        }
        vec2d operator *(const vec2d& a) const
        {
            return vec2d(x * a.x, y * a.y);
        }
        vec2d operator /(const vec2d& a) const
        {
            return vec2d(x / a.x, y / a.y);
        }
        vec2d operator |(const vec2d& a) const
        {
            return vec2d(x | a.x, y | a.y);
        }
        vec2d operator &(const vec2d& a) const
        {
            return vec2d(x & a.x, y & a.y);
        }
        vec2d operator ^(const vec2d& a) const
        {
            return vec2d(x ^ a.x, y ^ a.y);
        }
        //Scalar multiplication and division
        vec2d operator *(const fixpoint a) const
        {
            return vec2d(x*a, y*a);
        }
        vec2d operator /(const fixpoint a) const
        {
            return vec2d(x/a, y/a);
        }

        //Geometric vector functions
        fixpoint dot (const vec2d& a) const
        {
            return (x*a.x + y*a.y);
        }
        //Cross product doesn't really exist in 2d. We just project it into 3d
        //and return the k component
        fixpoint cross (const vec2d& a) const
        {
            return (x*a.y - y*a.x);
        }
        fixpoint angle() const;
        fixpoint abs() const;
        fixpoint length() const
        {
            return abs();
        }
        fixpoint mag() const
        {
            return abs();
        }

        //Returns a unit vector in the same direction as this vector
        vec2d unit() const;

        //Makes this vector of unit length preserving the direction
        vec2d& normalize();

        //Returns a reflected vector
        //n is assumed to be a unit vector. Reflected vector points away from point
        //of contact
        vec2d reflect(const vec2d& n) const
        {
            return n * dot(n) * -2;
        }

        //Returns a matrix as a product of this as a column vector, and a as a row
        //vector
        mat22 outerprod(const vec2d& a) const;
    };

}

#endif


